﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using HeadCounter.Armourer;
using HeadCounter.Armourer.Base;
using MathematicallySafe.HeadCounter.Data.Common;
using MathematicallySafe.HeadCounter.Data.Domain;
using MathematicallySafe.HeadCounter.Data.Interfaces;
using MathematicallySafe.HeadCounter.Data.Parsers;
using StructureMap;

namespace MathematicallySafe.HeadCounter.Data.Models
{
    public class LootTableModel : ILootTable, IDisposable
    {
        private IOptions _options;
        private IArmouryService _armouryService;
        private LootTable _lootTable;

        private bool _isDirty;

        private readonly ItemXmlParser _xmlParser = new ItemXmlParser(); // todo Dependency injection?

        public event EventHandler<ItemDownloadedEventArgs> ItemDownloaded;

        private readonly object _sync = new object();

        public LootTableModel(IArmouryService armouryService, IOptions options)
        {
            _options = options;
            _armouryService = armouryService;

            if (File.Exists(Constants.LoottableFileName))
            {
                Load();
            }
            else
            {
                CreateDefault();
            }

            SubscribeEvents();
        }

        private void CreateDefault()
        {
            _lootTable = new LootTable
            {
                Items = new List<LootItem>()
            };
        }

        public void SubscribeEvents()
        {
            _armouryService.ItemDataDownloaded += ArmouryServiceItemDownloaded;
        }

        public void UnsubscribeEvents()
        {
            _armouryService.ItemDataDownloaded -= ArmouryServiceItemDownloaded;
        }

        public List<LootItem> Items
        {
            get { return _lootTable.Items; }
            set { _lootTable.Items = value; }
        }

        public LootItem this[string itemName]
        {
            get
            {
                lock (_sync)
                {
                    var item = Items.Where(i => i.Name == itemName).FirstOrDefault();
                    return item;
                }
            }
        }

        public LootItem this[int id]
        {
            get
            {
                lock (_sync)
                {
                    var item = Items.Where(i => i.ID == id).FirstOrDefault();
                    return item;
                }
            }
        }

        public bool HasItem(int id)
        {
            lock (_sync)
            {
                return Items.FirstOrDefault(i => i.ID == id) != null;
            }
        }

        public void DownloadItemAsynch(int id, bool force)
        {
            lock (_sync)
            {
                var item = Items.Where(i => i.ID == id).FirstOrDefault();

                if (item == null || force)
                {
                    _armouryService.EnqueueGetItem(id);
                    _armouryService.RunArmouryServiceAsync();
                }
            }
        }

        void ArmouryServiceItemDownloaded(object sender, ItemDataDownloadedEventArgs e)
        {
            LootItem item; 

            lock (_sync)
            {
                item = _xmlParser.MakeLootItem(e.ItemXml);

                if (item.iLevel >= 200)
                {
                    var match = Items.FirstOrDefault(i => i.ID == item.ID);

                    if (match != null)
                    {
                        Items.Remove(match);
                    }

                    Items.Add(item);
                    Items = (Items.OrderBy(i => i.Name)).ToList();
                }
            }

            if (item != null && ItemDownloaded != null)
            {
                ItemDownloaded(this, new ItemDownloadedEventArgs(item.ID));
            }
        }

        public void ProcessLootOverrides()
        {
            Items.ForEach(item =>
            {
                var lootOverride = _options.Overrides.FirstOrDefault(o => o.id == item.ID);

                if (lootOverride != null)
                {
                    item.Slot = lootOverride.Slot;
                    item.iLevel = lootOverride.iLevel;
                }
            });
        }

        public bool IsDirty
        {
            get { return _isDirty; }
        }

        public bool IsClean
        {
            get { return !_isDirty; }
        }

        public void SetClean()
        {
            _isDirty = false;
        }

        public void SetDirty()
        {
            _isDirty = true;
        }

        private void Load()
        {
            _lootTable = SerialisationHelper.Load<LootTable>(Constants.LoottableFileName);
        }

        public void Save()
        {
            SerialisationHelper.Save<LootTable>(Constants.LoottableFileName, _lootTable);
        }

        public void Dispose()
        {
            UnsubscribeEvents();
        }
    }

}